1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package groovy.test;
20
21 import groovy.lang.Closure;
22 import groovy.lang.GroovyRuntimeException;
23 import groovy.lang.GroovyShell;
24 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
25 import org.junit.Test;
26
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29 import java.util.concurrent.atomic.AtomicInteger;
30 import java.util.logging.Logger;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class GroovyAssert extends org.junit.Assert {
61
62 private static final Logger log = Logger.getLogger(GroovyAssert.class.getName());
63
64 private static final int MAX_NESTED_EXCEPTIONS = 10;
65 private static final AtomicInteger counter = new AtomicInteger(0);
66
67 public static final String TEST_SCRIPT_NAME_PREFIX = "TestScript";
68
69
70
71
72 protected static String genericScriptName() {
73 return TEST_SCRIPT_NAME_PREFIX + (counter.getAndIncrement()) + ".groovy";
74 }
75
76
77
78
79
80
81 public static void assertScript(final String script) throws Exception {
82 GroovyShell shell = new GroovyShell();
83 shell.evaluate(script, genericScriptName());
84 }
85
86
87
88
89
90
91
92 public static Throwable shouldFail(Closure code) {
93 boolean failed = false;
94 Throwable th = null;
95 try {
96 code.call();
97 } catch (GroovyRuntimeException gre) {
98 failed = true;
99 th = ScriptBytecodeAdapter.unwrap(gre);
100 } catch (Throwable e) {
101 failed = true;
102 th = e;
103 }
104 assertTrue("Closure " + code + " should have failed", failed);
105 return th;
106 }
107
108
109
110
111
112
113
114
115
116 public static Throwable shouldFail(Class clazz, Closure code) {
117 Throwable th = null;
118 try {
119 code.call();
120 } catch (GroovyRuntimeException gre) {
121 th = ScriptBytecodeAdapter.unwrap(gre);
122 } catch (Throwable e) {
123 th = e;
124 }
125
126 if (th == null) {
127 fail("Closure " + code + " should have failed with an exception of type " + clazz.getName());
128 } else if (!clazz.isInstance(th)) {
129 fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
130 }
131 return th;
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 public static Throwable shouldFailWithCause(Class expectedCause, Closure code) {
149 if (expectedCause == null) {
150 fail("The expectedCause class cannot be null");
151 }
152 Throwable cause = null;
153 Throwable orig = null;
154 int level = 0;
155 try {
156 code.call();
157 } catch (GroovyRuntimeException gre) {
158 orig = ScriptBytecodeAdapter.unwrap(gre);
159 cause = orig.getCause();
160 } catch (Throwable e) {
161 orig = e;
162 cause = orig.getCause();
163 }
164
165 if (orig != null && cause == null) {
166 fail("Closure " + code + " was expected to fail due to a nested cause of type " + expectedCause.getName() +
167 " but instead got a direct exception of type " + orig.getClass().getName() + " with no nested cause(s). Code under test has a bug or perhaps you meant shouldFail?");
168 }
169
170 while (cause != null && !expectedCause.isInstance(cause) && cause != cause.getCause() && level < MAX_NESTED_EXCEPTIONS) {
171 cause = cause.getCause();
172 level++;
173 }
174
175 if (orig == null) {
176 fail("Closure " + code + " should have failed with an exception having a nested cause of type " + expectedCause.getName());
177 } else if (cause == null || !expectedCause.isInstance(cause)) {
178 fail("Closure " + code + " should have failed with an exception having a nested cause of type " + expectedCause.getName() + ", instead found these Exceptions:\n" + buildExceptionList(orig));
179 }
180 return cause;
181 }
182
183
184
185
186
187
188
189
190
191 public static Throwable shouldFail(Class clazz, String script) {
192 Throwable th = null;
193 try {
194 GroovyShell shell = new GroovyShell();
195 shell.evaluate(script, genericScriptName());
196 } catch (GroovyRuntimeException gre) {
197 th = ScriptBytecodeAdapter.unwrap(gre);
198 } catch (Throwable e) {
199 th = e;
200 }
201
202 if (th == null) {
203 fail("Script should have failed with an exception of type " + clazz.getName());
204 } else if (!clazz.isInstance(th)) {
205 fail("Script should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
206 }
207 return th;
208 }
209
210
211
212
213
214
215
216 public static Throwable shouldFail(String script) {
217 boolean failed = false;
218 Throwable th = null;
219 try {
220 GroovyShell shell = new GroovyShell();
221 shell.evaluate(script, genericScriptName());
222 } catch (GroovyRuntimeException gre) {
223 failed = true;
224 th = ScriptBytecodeAdapter.unwrap(gre);
225 } catch (Throwable e) {
226 failed = true;
227 th = e;
228 }
229 assertTrue("Script should have failed", failed);
230 return th;
231 }
232
233
234
235
236 private static final ThreadLocal<Boolean> notYetImplementedFlag = new ThreadLocal<Boolean>();
237
238
239
240
241
242
243
244 private static Method findRunningJUnitTestMethod(Class caller) {
245 final Class[] args = new Class[]{};
246
247
248 final Throwable t = new Exception();
249 for (int i = t.getStackTrace().length - 1; i >= 0; --i) {
250 final StackTraceElement element = t.getStackTrace()[i];
251 if (element.getClassName().equals(caller.getName())) {
252 try {
253 final Method m = caller.getMethod(element.getMethodName(), args);
254 if (isPublicTestMethod(m)) {
255 return m;
256 }
257 }
258 catch (final Exception e) {
259
260 }
261 }
262 }
263 throw new RuntimeException("No JUnit test case method found in call stack");
264 }
265
266
267
268
269
270
271
272 private static boolean isPublicTestMethod(final Method method) {
273 final String name = method.getName();
274 final Class[] parameters = method.getParameterTypes();
275 final Class returnType = method.getReturnType();
276
277 return parameters.length == 0
278 && (name.startsWith("test") || method.getAnnotation(Test.class) != null)
279 && returnType.equals(Void.TYPE)
280 && Modifier.isPublic(method.getModifiers());
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 public static boolean notYetImplemented(Object caller) {
319 if (notYetImplementedFlag.get() != null) {
320 return false;
321 }
322 notYetImplementedFlag.set(Boolean.TRUE);
323
324 final Method testMethod = findRunningJUnitTestMethod(caller.getClass());
325 try {
326 log.info("Running " + testMethod.getName() + " as not yet implemented");
327 testMethod.invoke(caller, (Object[]) new Class[]{});
328 fail(testMethod.getName() + " is marked as not yet implemented but passes unexpectedly");
329 }
330 catch (final Exception e) {
331 log.info(testMethod.getName() + " fails which is expected as it is not yet implemented");
332
333 }
334 finally {
335 notYetImplementedFlag.set(null);
336 }
337 return true;
338 }
339
340 private static String buildExceptionList(Throwable th) {
341 StringBuilder sb = new StringBuilder();
342 int level = 0;
343 while (th != null) {
344 if (level > 1) {
345 for (int i = 0; i < level - 1; i++) sb.append(" ");
346 }
347 if (level > 0) sb.append("-> ");
348 if (level > MAX_NESTED_EXCEPTIONS) {
349 sb.append("...");
350 break;
351 }
352 sb.append(th.getClass().getName()).append(": ").append(th.getMessage()).append("\n");
353 if (th == th.getCause()) {
354 break;
355 }
356 th = th.getCause();
357 level++;
358 }
359 return sb.toString();
360 }
361
362 }